/*
 * fixedtreenode.cpp
 *
 *  Created on: Mar 29, 2011
 *      Author: Mo
 */

#include "fixedtreenode.h"
#include <vector>
#include <assert.h>
#include <iostream>

int FixedTreeNode::getFeatureToCheck () {return this->featureToCheck;}
void FixedTreeNode::setFeatureToCheck (int feature) {this->featureToCheck = feature;}
int FixedTreeNode::getRemainingInstances() {return this->remainingInstances;}
void FixedTreeNode::setRemainingInstances (int remainingInstances) {this->remainingInstances = remainingInstances;}
int FixedTreeNode::getClassifiedInstances() {return this->classifiedInstances;}
void FixedTreeNode::setClassifiedInstances(int ci) {this->classifiedInstances = ci;}
void FixedTreeNode::setNodeRB (RuleBase newRB){this->nodeRB = RuleBase(newRB);}
RuleBase FixedTreeNode::getChildRB(int i){return this->childRB[i];}
void FixedTreeNode::setNodeDecision(decision d){this->nodeDecision = d;}
decision FixedTreeNode::getNodeDecision(void){return this->nodeDecision;}


void FixedTreeNode::internal_updateChildRBs (void)
{
	int ruleSize = this->nodeRB.getInstanceSize();
	if (this->nodeRB.getInstanceNum() > 0)
	{
		int featureValue;
		for (int i = 0; i != this->nodeRB.getInstanceNum(); i++)
		{
			featureValue = this->nodeRB.getFeature(i, this->featureToCheck);
			//we will update MGA later. This way we won't need to update it after each time we add an instance to it.
			this->childRB[featureValue].addInstanceNOUpdateMGA(this->nodeRB.getInstance(i)); 
		}
		for (int i = 0; i != CELLSTATES; i++) //childRBs are created here but their MGA are not sorted.
		{
			this->childRB[i].updateMostGainArray();
		}
		this->classifiedInstances = 0;
		for (int i = 0; i != CELLSTATES; i++)
		{
			if (this->childRB[i].getInstanceNum() > 0)
			{
				//this->childRB[i].updateMostGainArray(); All RB objects have always this array sorted correctly now.
				if (this->childRB[i].allSameClass())
					this->classifiedInstances += this->childRB[i].getInstanceNum();
			}
		}
		this->remainingInstances = this->nodeRB.getInstanceNum() - this->classifiedInstances;

		for (int i = 0; i != ruleSize; i++) //now creating the "nodeFeatureAvailable" array
			this->childFeatureAvailable[i] = this->nodeFeatureAvailable[i];
		this->childFeatureAvailable[this->featureToCheck] = false; //this feature was used here.
	}
	else
	{
		for (int i = 0; i != ruleSize; i++)
			this->childFeatureAvailable[i] = false;
	}
}

int FixedTreeNode::featureWithMostGain()
{
	return this->mostGainArray[0];
}

void FixedTreeNode::updateChildRBsBest ()
{
	this->featureToCheck = this->mostGainArray[0];
	this->internal_updateChildRBs();
}

void FixedTreeNode::updateChildRBs ()
{
	this->featureToCheck = this->mostGainArray[0];
	this->internal_updateChildRBs();
}

void FixedTreeNode::updateChildRBs (int featureIndex)
{
	this->featureToCheck = featureIndex;
	this->internal_updateChildRBs();
}

void FixedTreeNode::updateChildRBBestAvailable()
{
	int i = 0;
	int bestAvailableFeature = 0;
	int ruleSize = this->nodeRB.getInstanceSize(); 
	
	while (i < ruleSize && this->nodeFeatureAvailable[this->mostGainArray[i]] == false) //finding the best available feature
		i++;
	assert (i < ruleSize && "Error: The program was trying to find the next best feature but all the features are used before."
		&& "This usually is a result of a conflict in the rule base.");
	//if (i == ruleSize)
		//cout << "Error: The program was trying to find the next best feature but all the features are used before."
			//<< "This usually is a result of a conflict in the rule base." << endl;
	
	bestAvailableFeature = this->mostGainArray[i];
	this->updateChildRBs(bestAvailableFeature);

// This is done in internale_update childRB
//	for (i = 0; i != ruleSize; i++)
//		this->childFeatureAvailable[i] = this->nodeFeatureAvailable[i];
//	this->childFeatureAvailable[this->featureToCheck] = false; //this feature was used here.
}

void FixedTreeNode::setAsRoot(RuleBase& rb)
{
	if (rb.getInstanceNum() > 0) // if the rule base is not empty
	{
		int ruleSize = rb.getInstanceSize();
		//rb.updateMostGainArray(); - No need for that, RuleBase object always keep this array updated.
		this->nodeRB = RuleBase(rb);
		int* tempGainArray = new int[ruleSize];
		rb.copyMostGainArray(tempGainArray, ruleSize);
		for (int i = 0; i != rb.getInstanceSize(); i++)
		{
			this->mostGainArray[i] = tempGainArray[i]; // mGA of the node is the same as mGA of the RB
			this->nodeFeatureAvailable[i] = true;
		}
		this->featureToCheck = this->mostGainArray[0]; // we form the childRBs based on the best feature
		if (this->nodeRB.allSameClass())
			if (this->nodeRB.getLabel(0) == WHITE)
				this->nodeDecision = dwhite;
			else //if (nrb.getLabel(0) == BLACK)
				this->nodeDecision = dblack;
		else
		{
			this->nodeDecision = dnotYet;
			this->internal_updateChildRBs(); // this->featureToCheck is valid at this point. Also if decision is made for either black or white, then there is no child.
		}
		delete[] tempGainArray;
	}
	else
		this->nodeDecision = dblank;
}

void FixedTreeNode::updateFromParent (int whichChild, FixedTreeNode& parent)
{

	if (parent.childRB[whichChild].getInstanceNum() > 0 && parent.nodeDecision == dnotYet) // if the rule base is not empty
	{
		int ruleSize = parent.nodeRB.getInstanceSize(); // can it be this->nodeRB.getInstantSize()?
		this->nodeRB = RuleBase(parent.childRB[whichChild]);
		int* tempGainArray = new int[ruleSize];
		parent.nodeRB.copyMostGainArray(tempGainArray, ruleSize); // can it be this->nodeRB.copyMostGainArray(tempGainArray, ruleSize)?
		for (int i = 0; i != this->nodeRB.getInstanceSize(); i++)
		{
			this->mostGainArray[i] = tempGainArray[i]; // mGA of the node is the same as mGA of the RB
			this->nodeFeatureAvailable[i] = parent.childFeatureAvailable[i];
		}//at this point we have copied all the information the parent could provide to us.
		if (this->nodeRB.allSameClass())
			if (this->nodeRB.getLabel(0) == WHITE)
				this->nodeDecision = dwhite;
			else //if (nrb.getLabel(0) == BLACK)
				this->nodeDecision = dblack;
		else
		{
			this->nodeDecision = dnotYet;
			this->updateChildRBBestAvailable(); // both mostGainArray[] and featureAvailableArray[] are valid here.
		}
		delete[] tempGainArray;
	}
	else
		this->nodeDecision = dblank;

}

void FixedTreeNode::makeArraysOfBlankNode (int ruleSize)
{
//	assert (this->childFeatureAvailable == (bool*)0xcdcdcdcd &&
//			this->nodeFeatureAvailable == (bool*)0xcdcdcdcd &&
//			this->mostGainArray.size() == 0 &&
//			"Error: makeArraysOfBlankNode() was called for a non-blank node.");

	assert (this->childFeatureAvailable == NULL &&
			this->nodeFeatureAvailable == NULL &&
			this->mostGainArray.size() == 0 &&
			"Error: makeArraysOfBlankNode() was called for a non-blank node.");

	this->childFeatureAvailable = new bool [ruleSize];
	this->nodeFeatureAvailable = new bool [ruleSize];
	this->mostGainArray.resize(ruleSize);
		
	for (int i = 0; i != ruleSize; i++)
	{
		this->childFeatureAvailable[i] = false;
		this->nodeFeatureAvailable[i] = false;
		this->mostGainArray[i] = -1;
	}
	for (int i = 0; i != CELLSTATES; i++)
		this->childRB[i].setDynamicSizes(ruleSize);
}

FixedTreeNode& FixedTreeNode::operator=(FixedTreeNode& other)
{
	if (&other == this)
		return *this;
	int ruleSize = other.nodeRB.getInstanceSize();

	this->childFeatureAvailable = new bool [ruleSize];
	this->nodeFeatureAvailable = new bool [ruleSize];
	this->mostGainArray.resize(ruleSize);
	for (int i = 0; i != ruleSize; i++)
	{
		this->childFeatureAvailable[i] = other.childFeatureAvailable[i];
		this->nodeFeatureAvailable[i] = other.nodeFeatureAvailable[i];
		this->mostGainArray[i] = other.mostGainArray[i];
	}
	this->nodeRB = RuleBase(other.nodeRB);
	this->classifiedInstances = other.classifiedInstances;
	this->featureToCheck = other.featureToCheck;
	this->remainingInstances = other.remainingInstances;
	this->nodeDecision = other.nodeDecision;
	for (int i = 0; i != CELLSTATES; i++)
		this->childRB[i] = RuleBase(other.childRB[i]);

	return *this;
}


FixedTreeNode::FixedTreeNode()
{
	this->nodeDecision = dblank;
	this->featureToCheck = -1;
	this->classifiedInstances = -1;
	this->featureToCheck = -1;
	this->remainingInstances = -1;
}

FixedTreeNode::FixedTreeNode(int feature, RuleBase& nrb)
{

	if (nrb.getInstanceNum() > 0) // if the rule base is not empty
	{
		int ruleSize = nrb.getInstanceSize();

		this->childFeatureAvailable = new bool [ruleSize];
		this->nodeFeatureAvailable = new bool [ruleSize];
		this->mostGainArray.resize(ruleSize);
		for (int i = 0; i != ruleSize; i++)
		{
			this->childFeatureAvailable[i] = false;
		}

		this->featureToCheck = feature; // we form the childRBs based on the given feature
		this->nodeRB = RuleBase(nrb);
		int* tempGainArray = new int[ruleSize];
		nrb.copyMostGainArray(tempGainArray, ruleSize);
		for (int i = 0; i != ruleSize; i++)
		{
			this->mostGainArray[i] = tempGainArray[i]; // mGA of the node is the same as mGA of the RB
			this->nodeFeatureAvailable[i] = true; //childFeatureAvailable[] is set in updateChildRBs().
		}

		if (nrb.allSameClass())
			if (nrb.getLabel(0) == WHITE)
				this->nodeDecision = dwhite;
			else //if (nrb.getLabel(0) == BLACK)
				this->nodeDecision = dblack;
		else
		{
			this->nodeDecision = dnotYet;
			this->updateChildRBs(this->featureToCheck); // if decision is made for either black or white, then there is no child.
		}
		delete[] tempGainArray;
	}
	else
		this->nodeDecision = dblank;	
}

FixedTreeNode::FixedTreeNode(RuleBase& nrb)
{
	int ruleSize = nrb.getInstanceSize();

	this->childFeatureAvailable = new bool [ruleSize];
	this->nodeFeatureAvailable = new bool [ruleSize];
	this->mostGainArray.resize(ruleSize);

	if (nrb.getInstanceNum() > 0) // if the rule base is not empty
	{
		this->featureToCheck = this->mostGainArray[0]; // we form the childRBs based on the best feature
		this->nodeRB = RuleBase(nrb);
		int* tempGainArray = new int[ruleSize];
		nrb.copyMostGainArray(tempGainArray, ruleSize);
		for (int i = 0; i != ruleSize; i++)
		{
			this->mostGainArray[i] = tempGainArray[i]; // mGA of the node is the same as mGA of the RB
			this->nodeFeatureAvailable[i] = true; //childFeatureAvailable[] is set in updateChildRBs().
		}
		if (nrb.allSameClass())
			if (nrb.getLabel(0) == WHITE)
				this->nodeDecision = dwhite;
			else //if (nrb.getLabel(0) == BLACK)
				this->nodeDecision = dblack;
		else
		{
			this->nodeDecision = dnotYet;
			this->updateChildRBs(this->featureToCheck); // if decision is made for either black or white, then there is no child.
		}
		delete[] tempGainArray;
	}
	else
		this->nodeDecision = dblank;
}


FixedTreeNode::FixedTreeNode(FixedTreeNode& other)
{
	int ruleSize = other.nodeRB.getInstanceSize();

	this->childFeatureAvailable = new bool [ruleSize];
	this->nodeFeatureAvailable = new bool [ruleSize];
	this->mostGainArray.resize(ruleSize);
	for (int i = 0; i != ruleSize; i++)
	{
		this->childFeatureAvailable[i] = other.childFeatureAvailable[i];
		this->nodeFeatureAvailable[i] = other.nodeFeatureAvailable[i];
		this->mostGainArray[i] = other.mostGainArray[i];
	}
	this->nodeRB = RuleBase(other.nodeRB);
	this->classifiedInstances = other.classifiedInstances;
	this->featureToCheck = other.featureToCheck;
	this->remainingInstances = other.remainingInstances;
	this->nodeDecision = other.nodeDecision;
	for (int i = 0; i != CELLSTATES; i++)
		this->childRB[i] = RuleBase(other.childRB[i]);

}

FixedTreeNode::~FixedTreeNode()
{
	
	this->classifiedInstances = 0;
	this->featureToCheck = 0;
	this->remainingInstances = 0;
	this->nodeDecision = dwhite;
	delete[] childFeatureAvailable;
	delete[] nodeFeatureAvailable;
}

